<?php
/**
*	@author SignpostMarv Martin
*	@package LLSD
*/

/**
*	Does the grunt work of converting an LLSD document into a native PHP data type
*	@package LLSD
*	@uses Iterator
*/
class uhu_LLSD_struct extends uhu_LLSD_common implements Iterator
{
/**
*	@var mixed the native PHP data type for the LLSD node
*/
	protected $struct;
/**
*	@var string Holds the transcoded LLSD document.
*/
	protected $LLSD;
/**
*	Builds an isntance of uhu_LLSD_struct.
*	@param SimpleXMLElement The LLSD document to convert to a native PHP data type
*	@uses SimpleXMLElement::getName()
*	@uses SimpleXMLElement::children()
*	@uses uhu_LLSD_struct::struct_build_array()
*	@uses uhu_LLSD_struct::struct_build_map)
*	@uses uhu_LLSD_struct::struct_build_atomic()
*	@uses uhu_LLSD_exception
*/
	public function __construct(SimpleXMLElement $element)
	{
		if($element->getName() !== 'llsd')
		{
			throw new uhu_LLSD_exception('root element must be llsd.',101);
		}
		else
		{
			foreach($element->children() as $child)
			{
				switch($child->getName())
				{
					case 'array':
						self::struct_build_array($this->struct,$child);
					break;
					case 'map':
						self::struct_build_map($this->struct,$child);
					break;
					default:
						self::struct_handle_atomic($this->struct,$child);
					break;
				}
			}
		}
	}
/**
*	Part of the Iterator interface
*	@return mixed
*/
	public function current()
	{
		return current($this->struct);
	}
/**
*	Part of the Iterator interface
*	@return mixed
*/
	public function key()
	{
		return key($this->struct);
	}
/**
*	Part of the Iterator interface
*	@return mixed
*/
	public function next()
	{
		return next($this->struct);
	}
/**
*	Part of the Iterator interface
*	@return bool
*/
	public function rewind()
	{
		return reset($this->struct);
	}
/**
*	Part of the Iterator interface
*	@return bool
*/
	public function valid()
	{
		return (bool)$this->current();
	}
/**
*	Returns the native PHP data type of the parsed LLSD document
*	@return mixed
*	@uses uhu_LLSD_struct::$struct
*/
	public function struct()
	{
		return $this->struct;
	}
/**
*	Returns the transcoded LLSD document
*	@return string
*	@uses uhu_LLSD_struct::$LLSD
*	@uses uhu_LLSD::save()
*/
	public function LLSD($indent=null)
	{
		if(isset($this->LLSD) === false)
		{
			$this->LLSD = uhu_LLSD::save($this->struct(),$indent);
		}
		return $this->LLSD;
	}
/**
*	Converts an LLSD array node into a native PHP array
*	@param mixed Either uhu_LLSD_struct::$struct or a sub-element of uhu_LLSD_struct::$struct
*	@param SimpleXMLElement The LLSD node to convert to a PHP array
*	@return void
*	@uses uhu_LLSD_struct::struct_build_atomic()
*	@uses uhu_LLSD_struct::struct_build_array()
*	@uses uhu_LLSD_struct::struct_build_map()
*/
	protected static function struct_build_array(& $node_struct,SimpleXMLElement $array_element)
	{
		$node_struct = array();
		foreach($array_element->children() as $child)
		{
			switch($child->getName())
			{
				case 'undef':
				case 'boolean':
				case 'integer':
				case 'real':
				case 'uuid':
				case 'string':
				case 'date':
				case 'uri':
				case 'binary':
					self::struct_handle_atomic($node_struct[],$child);
				break;
				case 'array':
					self::struct_build_array($node_struct[],$child);
				break;
				case 'map':
					self::struct_build_map($node_struct[],$child);
				break;
			}
		}
	}
/**
*	Converts an LLSD map node into a native PHP array
*	@param mixed Either uhu_LLSD_struct::$struct or a sub-element of uhu_LLSD_struct::$struct
*	@param SimpleXMLElement The LLSD node to convert to a PHP array
*	@return void
*	@uses uhu_LLSD_struct::struct_validate_map_contents()
*	@uses uhu_LLSD_struct::struct_build_atomic()
*	@uses uhu_LLSD_struct::struct_build_array()
*	@uses uhu_LLSD_struct::struct_build_map()
*/
	protected static function struct_build_map(& $node_struct,SimpleXMLElement $map_element)
	{
		$node_struct = array();
		self::struct_validate_map_contents($map_element);
		$children = $map_element->children();
		$loop = count($children) / 2;
		for($x=0;$x < $loop;++$x)
		{
			switch($children[($x * 2) + 1]->getName())
			{
				case 'undef':
				case 'boolean':
				case 'integer':
				case 'real':
				case 'uuid':
				case 'string':
				case 'date':
				case 'uri':
				case 'binary':
					self::struct_handle_atomic($node_struct[(string)$children[$x * 2]],$children[($x * 2) + 1]);
				break;
				case 'map':
					self::struct_build_map($node_struct[(string)$children[$x * 2]],$children[($x * 2) + 1]);
				break;
				case 'array':
					self::struct_build_array($node_struct[(string)$children[$x * 2]],$children[($x * 2) + 1]);
				break;
			}
		}
	}
/**
*	Ensures that the LLSD node supplied is a map element
*	@param SimpleXMLElement The LLSD node to validate
*	@uses uhu_LLSD_exception
*	@uses SimpleXMLElement::children()
*	@uses SimpleXMLElement::getName()
*/
	protected static function struct_validate_map_contents(SimpleXMLElement $map_element)
	{
		if($map_element->getName() !== 'map')
		{
			throw new uhu_LLSD_exception('only map elements should be passed to uhu_LLSD::struct_validate_map_contents',106);
		}
		else
		{
			if(($count = count($map_element->children())) < 2)
			{
				throw new uhu_LLSD_exception('map elements must have at least 2 children.',106);
			}
			else if(($count % 2) !== 0)
			{
				throw new uhu_LLSD_exception('the number of child elements to a map element must be a multiple of 2',106);
			}
			else
			{
				$loop = $count / 2;
				$children = $map_element->children();
				for($x=0;$x < $loop;++$x)
				{
					if(($last_element_name = $children[$x * 2]->getName()) !== 'key')
					{
						throw new uhu_LLSD_exception('key element expected, ' . $last_element_name . ' found.',106);
					}
					else if($children[($x * 2) + 1]->getName() === 'key')
					{
						throw new uhu_LLSD_exception('key pair value cannot be key element.',106);
					}
				}
			}
		}
	}
/**
*	Converts an LLSD node into an appropriate native PHP type
*	@param mixed Either uhu_LLSD_struct::$struct or a sub-element of uhu_LLSD_struct::$struct
*	@param SimpleXMLElement The LLSD node to convert to a antive PHP data type
*	@return void
*	@uses uhu_LLSD_struct::struct_build_undef()
*	@uses uhu_LLSD_struct::struct_build_bool()
*	@uses uhu_LLSD_struct::struct_build_integer()
*	@uses uhu_LLSD_struct::struct_build_real()
*	@uses uhu_LLSD_struct::struct_build_uuid()
*	@uses uhu_LLSD_struct::struct_build_string()
*	@uses uhu_LLSD_struct::struct_build_date()
*	@uses uhu_LLSD_struct::struct_build_uri()
*	@uses uhu_LLSD_struct::struct_build_binary()
*/
	protected static function struct_handle_atomic(& $element,SimpleXMLElement $child)
	{
		switch($child->getName())
		{
			case 'undef':
				self::struct_build_undef($element,$child);
			break;
			case 'boolean':
				self::struct_build_bool($element,$child);
			break;
			case 'integer':
				self::struct_build_integer($element,$child);
			break;
			case 'real':
				self::struct_build_real($element,$child);
			break;
			case 'uuid':
				self::struct_build_uuid($element,$child);
			break;
			case 'string':
				self::struct_build_string($element,$child);
			break;
			case 'date':
				self::struct_build_date($element,$child);
			break;
			case 'uri':
				self::struct_build_uri($element,$child);
			break;
			case 'binary':
				self::struct_build_binary($element,$child);
			break;
		}
	}
/**
*	Intended to convert a LLSD undef element into null
*	@param mixed Either uhu_LLSD_struct::$struct or a sub-element of uhu_LLSD_struct::$struct
*	@param SimpleXMLElement The LLSD node to convert to a antive PHP data type
*	@return void
*/
	protected static function struct_build_undef(& $element,SimpleXMLElement $undef)
	{
		$element = null;
	}
/**
*	Intended to convert a LLSD bool element into a native PHP boolean
*	@param mixed Either uhu_LLSD_struct::$struct or a sub-element of uhu_LLSD_struct::$struct
*	@param SimpleXMLElement The LLSD node to convert to a antive PHP data type
*	@return void
*/
	protected static function struct_build_bool(& $element,SimpleXMLElement $bool)
	{
		if(empty($bool))
		{
			$element = false;
		}
		else
		{
			switch(strtolower((string)$bool))
			{
				case '1':
				case 'true':
					$element = true;
				break;
				default:
					$element = false;
				break;
			}
		}
	}
/**
*	Intended to convert a LLSD integer element into a native PHP integer, or an instance of uhu_LLSD_integer_NaN
*	@param mixed Either uhu_LLSD_struct::$struct or a sub-element of uhu_LLSD_struct::$struct
*	@param SimpleXMLElement The LLSD node to convert to a antive PHP data type
*	@return void
*	@uses uhu_LLSD_integer_NaN::singleton()
*/
	protected static function struct_build_integer(& $element,SimpleXMLElement $integer)
	{
		$element = (strtolower((string)$integer) === 'nan') ? uhu_LLSD_integer_NaN::singleton() : (integer)$integer;
	}
/**
*	Intended to convert a LLSD real element into a native PHP float, or an instance of uhu_LLSD_real_NaN
*	@param mixed Either uhu_LLSD_struct::$struct or a sub-element of uhu_LLSD_struct::$struct
*	@param SimpleXMLElement The LLSD node to convert to a antive PHP data type
*	@return void
*	@uses uhu_LLSD_real_NaN::singleton()
*/
	protected static function struct_build_real(& $element,SimpleXMLElement $real)
	{
		$element = (strtolower((string)$real) === 'nan') ? uhu_LLSD_real_NaN::singleton() : (float)$real;
	}
/**
*	Intended to convert a LLSD uuid element into an instance of uhu_LLSD_UUID
*	@param mixed Either uhu_LLSD_struct::$struct or a sub-element of uhu_LLSD_struct::$struct
*	@param SimpleXMLElement The LLSD node to convert to an instance of uhu_LLSD_UUID
*	@return void
*	@uses uhu_LLSD_UUID()
*/
	protected static function struct_build_uuid(& $element,SimpleXMLElement $uuid)
	{
		$element = new uhu_LLSD_UUID($uuid);
	}
/**
*	Intended to convert a LLSD bool element into a native PHP string
*	@param mixed Either uhu_LLSD_struct::$struct or a sub-element of uhu_LLSD_struct::$struct
*	@param SimpleXMLElement The LLSD node to convert to a antive PHP data type
*	@return void
*/
	protected static function struct_build_string(& $element,SimpleXMLElement $string)
	{
		$element = (string)$string;
	}
/**
*	Intended to convert a LLSD date element into an instance of uhu_LLSD_date
*	@param mixed Either uhu_LLSD_struct::$struct or a sub-element of uhu_LLSD_struct::$struct
*	@param SimpleXMLElement The LLSD node to convert to an instance of uhu_LLSD_date
*	@return void
*	@uses uhu_LLSD_date()
*/
	protected static function struct_build_date(& $element,SimpleXMLElement $date)
	{
		$element = new uhu_LLSD_date($date);
	}
/**
*	Intended to convert a LLSD uri element into an instance of uhu_LLSD_uri
*	@param mixed Either uhu_LLSD_struct::$struct or a sub-element of uhu_LLSD_struct::$struct
*	@param SimpleXMLElement The LLSD node to convert to an instance of uhu_LLSD_uri
*	@return void
*	@uses uhu_LLSD_uri()
*/
	protected static function struct_build_uri(& $element,SimpleXMLElement $uri)
	{
		$element = new uhu_LLSD_uri($uri);
	}
/**
*	Intended to convert a LLSD binary element into an instance of uhu_LLSD_binary
*	@param mixed Either uhu_LLSD_struct::$struct or a sub-element of uhu_LLSD_struct::$struct
*	@param SimpleXMLElement The LLSD node to convert to an instance of uhu_LLSD_binary
*	@return void
*	@uses uhu_LLSD_binary()
*/
	protected static function struct_build_binary(& $element,SimpleXMLElement $binary)
	{
		$element = new uhu_LLSD_binary($binary);
	}
}
?>